#include <iostream>
#include <unistd.h>
#include <string.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#include "log.hpp"

enum{
    SOCKET_ERR = 2,
    BIND_ERR,
    LISTEN_ERR
};

const int backlog = 10;

class Sock
{
public:
    Sock()
    {}
    ~Sock()
    {}
public:
    void Socket()
    {
        _sockfd = socket(AF_INET, SOCK_STREAM,0);
        if(_sockfd < 0)
        {
            lg(FATAL,"socket err,%s:%d",strerror(errno),errno);
            exit(SOCKET_ERR);
        }
        int opt = 1;
        setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
    }

    void Bind(uint16_t port)
    {
        struct sockaddr_in local;
        local.sin_family = AF_INET;
        local.sin_port = htons(port);
        local.sin_addr.s_addr = INADDR_ANY;
        socklen_t len = sizeof(local);
        int n = bind(_sockfd,(struct sockaddr*)&local,len);
        if(n < 0)
        {
            lg(FATAL,"bind err,%s,%d",strerror(errno),errno);
            exit(BIND_ERR);
        }
    }

    void Listen()
    {
        int n = listen(_sockfd,backlog);
        if(n < 0)
        {
            lg(FATAL,"listen err,%s,%d",strerror(errno),errno);
            exit(LISTEN_ERR);
        }
    }

    int Accept(std::string *clientip, uint16_t *clientport)
    {
        struct sockaddr_in client;
        socklen_t len = sizeof(client);

        int n = accept(_sockfd,(struct sockaddr*)&client,&len);
        if(n < 0)
        {
            lg(WARNING, "accept error, %s: %d", strerror(errno), errno);
            return -1;
        }
        char in_buffer[64];
        inet_ntop(AF_INET,&client,in_buffer,sizeof(in_buffer));
        *clientip = in_buffer;
        *clientport = ntohs(client.sin_port);

        return n;
    }

    bool Connect(const std::string &ip, const uint16_t &port)
    {
        struct sockaddr_in server;
        memset(&server,0,sizeof(server));
        server.sin_family = AF_INET;
        server.sin_port = htons(port);
        inet_pton(AF_INET,ip.c_str(),&(server.sin_addr));
        
        socklen_t len = sizeof(server);
        int n = connect(_sockfd,(struct sockaddr*)&server,len);
        if(n < 0)
        {
            std::cerr << "connect to " << ip << ":" << port << " error" << std::endl;
            return false;
        }
        return true;
    }
    void Close()
    {
        close(_sockfd);
    }

    int Getsockfd()
    {
        return _sockfd;
    }

public:
    int _sockfd;
};